#include <sys/types.h>   // Definicao de tipos
#include <sys/socket.h>  // Biblioteca de estrutara de sockets
#include <netinet/in.h>  // Define os padroes de protocolo IP
#include <arpa/inet.h>   // Converte enderecos hosts
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h> // Define constantes POSIX
#include <errno.h>  // Utilizado para capturar mensagens de erro
#include <sys/time.h>

/*********************************************
Autor : Henrique Nishi - hsnishi@gmail.com
		Leandro Machado - l.fvm23@gmail.com
Descrição : Recebe 2 vetores de tamanho 250 por um socket, junta, e manda para o próximo nível. Fica esperando o vetor de 1000
			da raiz e manda pra baixo qunado recebe.
*********************************************/
//função para uso no quicksort
int compare(const void *a, const void *b)
{
	return (*(int*)a - *(int*)b);
}

int main(int argc, char *argv[ ])
{
	//struct feita para declaração de um socket
	struct sockaddr_in servidor, cliente, envio;
	struct hostent *host;
	int sock, sock_envio, novo_socket_aceito, 
		  tam_msg_recebida = 0, tam_socket_cliente, cont = 0, i,
		  dados_recebidos[500], vetor_aux1[250], vetor_total[1000], sockets_anteriores[2], vetor_desordenado[500];
	long int t_comunicacao[6], t_ordenacao;
	struct timeval inicio_ordenacao, final_ordenacao, inicio_comunicacao[6], final_comunicacao[6];
	
	if (argc < 3)
    {
        printf("Falta de argumentos");
        exit(0);
    }

	host = gethostbyname(argv[1]);

	//criando socket para protocolo ipv4 e tcp
	sock = socket(AF_INET,SOCK_STREAM,0);
	if(sock == -1)
	{
		perror("Erro na criação do socket");
		exit(1);
	}

	//familia do socket, AF_INET -> trabalha com protocolo ip
	servidor.sin_family = AF_INET;
	//porta do socket
	servidor.sin_port = htons(atoi(argv[2]));
	//endereco ip
	servidor.sin_addr.s_addr = INADDR_ANY;
	bzero(&(servidor.sin_zero),8);

	//associando o socket a uma porta
	if((bind(sock,(struct sockaddr *)&servidor,sizeof(struct sockaddr))) == -1)
	{
		perror("Erro ao associar socket à porta");
		close(sock);
		exit(1);
	}
	
	//fazer a porta ficar esperando alguma conexão
	if((listen(sock,10)) == -1)
	{
		perror("Erro na porta");
		exit(1);
	}
	
	//while para manter escutando novas conexões
	while(cont < 2)
	{
		//Pegando tamanho do cliente
		tam_socket_cliente = sizeof(struct sockaddr_in);

		//aceitando conexao e criando um novo socket pra ela
		novo_socket_aceito = accept(sock, (struct sockaddr*)&cliente,&tam_socket_cliente);
		
		// Pega o tempo inicial de comunicacao
		gettimeofday (&inicio_comunicacao[cont], NULL);
		
		tam_msg_recebida = recv(novo_socket_aceito, vetor_aux1, 250*4, MSG_WAITALL);
	 	
		// Pega o tempo final de comunicação
		gettimeofday (&final_comunicacao[cont], NULL);
		
		if(cont == 0)
		{
			for(i=0;i<250;i++)
			{
				dados_recebidos[i] = vetor_aux1[i];
				vetor_desordenado[i] = vetor_aux1[i];
			}
		}
		
		if(cont == 1)
		{
			for(i=0;i<250;i++)
			{
				dados_recebidos[i+250] = vetor_aux1[i];
				vetor_desordenado[i] = vetor_aux1[i];
			}
		}
		
		sockets_anteriores[cont] = novo_socket_aceito;
		cont++;	
	}
	
	// Pega o tempo inicial de ordenação
	gettimeofday (&inicio_ordenacao, NULL);
	
	//ordenar
	qsort(dados_recebidos, 500, sizeof(int), compare);
	
	// Pega o tempo final de ordenação
	gettimeofday (&final_ordenacao, NULL);
	
	
	//descritor para enviar pro servidor de cima
	sock_envio = socket(AF_INET, SOCK_STREAM, 0);
	if(sock_envio == -1)
	{
		perror("Erro na criação de socket");
		exit(1);
	}

    envio.sin_family = AF_INET;
    envio.sin_port = htons(atoi(argv[3]));
    envio.sin_addr = *((struct in_addr *)host->h_addr);		//endereço do server
    bzero(&(envio.sin_zero),8);

    if (connect(sock_envio, (struct sockaddr *)&envio, sizeof(struct sockaddr)) == -1)
    {
	  perror("Erro de conexao");
	  exit(1);
	}
	
	// Pega o tempo inicial de comunicacao
	gettimeofday (&inicio_comunicacao[2], NULL);
	
	//enviando msg pro servidor de cima
	send(sock_envio, dados_recebidos, 500*4, 0);	
	
	// Pega o tempo final de comunicação
	gettimeofday (&final_comunicacao[2], NULL);
	
	// Pega o tempo inicial de comunicacao
	gettimeofday (&inicio_comunicacao[3], NULL);
	
	//esperando msg de 1000 da raiz
	recv(sock_envio, vetor_total, 1000*4, 0);
	
	// Pega o tempo final de comunicação
	gettimeofday (&final_comunicacao[3], NULL);
	
	//enviando o de 1000 pra baixo
	for(i=0;i<2;i++){	
		// Pega o tempo inicial de comunicacao
		gettimeofday (&inicio_comunicacao[4+i], NULL);
		
		send(sockets_anteriores[i], vetor_total, 1000*4, 0);
		
		// Pega o tempo final de comunicação
		gettimeofday (&final_comunicacao[4+i], NULL);		
	}
		
	//fechando os sockets
	close(sock);
	close(sock_envio);

	// Calcula e mostra os tempos em milissegundos
	for(i=0;i<6;i++){
		t_comunicacao[i] = (long int)(1000000*(final_comunicacao[i].tv_sec - inicio_comunicacao[i].tv_sec) + (final_comunicacao[i].tv_usec - inicio_comunicacao[i].tv_usec));
		printf("\nTempo de comunicacao - %li microssegundos\n", t_comunicacao[i]);
	}
	t_ordenacao = (long int)(1000000*(final_ordenacao.tv_sec - inicio_ordenacao.tv_sec) + (final_ordenacao.tv_usec - inicio_ordenacao.tv_usec));
	printf("\nTempo ordenacao - %li microssegundos\n", t_ordenacao);	
	
	return 0;
}


